package com.wuhao.code.check.inspection.visitor

import com.aegis.mybatis.xmlless.enums.MethodType
import com.aegis.mybatis.xmlless.enums.XmllessDbType
import com.aegis.mybatis.xmlless.model.MethodInfo
import com.aegis.mybatis.xmlless.model.ResolvedQuery
import com.aegis.mybatis.xmlless.model.XmllessTableInfo
import com.aegis.mybatis.xmlless.model.base.BaseClassWrapper
import com.aegis.mybatis.xmlless.model.base.BaseMethodWrapper
import com.aegis.mybatis.xmlless.resolver.QueryResolver
import com.aegis.mybatis.xmlless.util.BuilderAssistantHolder
import com.aegis.mybatis.xmlless.util.TableInfoProvider
import com.aegis.mybatis.xmlless.util.XmllessInfoUtil
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.ide.highlighter.XmlFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
import com.intellij.testFramework.LightVirtualFile
import com.wuhao.code.check.model.KtClassWrapper
import com.wuhao.code.check.model.KtMethodWrapper
import com.wuhao.code.check.model.PsiClassWrapper
import com.wuhao.code.check.model.PsiMethodWrapper
import com.wuhao.code.check.ui.XmlResultDialog
import org.apache.ibatis.builder.MapperBuilderAssistant
import org.apache.ibatis.session.Configuration
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtNamedFunction

/**
 * @author 吴昊
 * @date 2024/01/18
 * @version 1.0
 * @since v1.0.0
 */
abstract class BaseXmllessGenerator(private val type: Int) : LocalQuickFix {

  companion object {
    fun createMethodInfo(
        modelName: String, classWrapper: BaseClassWrapper,
        methodWrapperCreator: (XmllessTableInfo) -> BaseMethodWrapper
    ): Pair<BaseMethodWrapper, MethodInfo> {
      val resource = modelName.replace('.', '/') + ".java (best guess)"
      val builderAssistant = MapperBuilderAssistant(Configuration(), resource).apply {
        currentNamespace = "np"
      }
      BuilderAssistantHolder.set(builderAssistant)
      XmllessInfoUtil.clearCache()
      val tableInfo = TableInfoProvider.getOrInitXmllessTableInfo(classWrapper)
      val methodWrapper = methodWrapperCreator(tableInfo)
      return methodWrapper to QueryResolver.resolveMethodInfo(
          methodWrapper,
          tableInfo,
          XmllessDbType.MYSQL,
          useCache = false
      )
    }
  }

  override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
    val element = descriptor.psiElement
    if (element is KtNamedFunction) {
      val modelType = getXmllessMapperModelType(element) as KtClass
      val modelName = modelType.fqName?.toString() ?: return
      create(modelName, project, KtClassWrapper(modelType)) { tableInfo ->
        KtMethodWrapper(element, KtClassWrapper(modelType), tableInfo)
      }
    } else if (element is PsiMethod) {
      val modelType = getXmllessMapperModelType(element) as PsiClass
      val modelName = modelType.qualifiedName ?: return
      val classWrapper = if (modelType is KtUltraLightClass) {
        KtClassWrapper(modelType.kotlinOrigin as KtClass)
      } else {
        PsiClassWrapper(modelType)
      }
      create(modelName, project, classWrapper) { tableInfo ->
        PsiMethodWrapper(element, classWrapper, tableInfo)
      }
    }
  }

  private fun create(
      modelName: String, project: Project,
      classWrapper: BaseClassWrapper,
      methodWrapperCreator: (XmllessTableInfo) -> BaseMethodWrapper
  ) {
    try {
      val (methodWrapper, methodInfo) = createMethodInfo(modelName, classWrapper, methodWrapperCreator)
      val query = QueryResolver.resolveQuery(methodInfo, false)
      val sql = query.sql
      if (sql != null) {
        if (type == 2) {
          createFile(project, methodWrapper, query)
        } else {
          val dialog = XmlResultDialog(project)
          dialog.setSql(sql)
          ApplicationManager.getApplication().invokeLater {
            dialog.show()
          }
        }
      } else {
        query.exception?.printStackTrace()
        ApplicationManager.getApplication().invokeLater {
          Messages.showErrorDialog(query.exception?.message ?: "", "解析失败")
        }
      }
    } catch (e: Exception) {
      e.printStackTrace()
      try {
        ApplicationManager.getApplication().invokeLater {
          Messages.showErrorDialog(e.message ?: "", "解析失败")
        }
      } catch (e:  Exception) {
        // do nothing
      }
    }
  }

  private fun createFile(
      project: Project,
      methodWrapper: BaseMethodWrapper,
      query: ResolvedQuery
  ) {
    val mapperName = methodWrapper.getFullName()
        .substring(0, methodWrapper.getFullName().lastIndexOf("."))
    val methodName = methodWrapper.getFullName()
        .substring(methodWrapper.getFullName().lastIndexOf(".") + 1)
    val tag = when (query.query!!.type) {
      MethodType.Clean,
      MethodType.Delete         -> "delete"

      MethodType.Insert,
      MethodType.InsertOrUpdate -> "insert"

      MethodType.Update,
      MethodType.LogicDelete    -> "update"

      MethodType.Select,
      MethodType.SelectDistinct,
      MethodType.Count          -> "select"
    }
    val content = wrapWithTag(
        tag, methodName,
        query.query!!.toSql().lines().joinToString("    \n") { "    $it" }.trim()
    )
    val xml = """<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="$mapperName">
$content
</mapper>
  """.trimIndent()
    val file = LightVirtualFile(
        "XMLLess 脚本",
        XmlFileType.INSTANCE, xml
    )
    val descriptor = OpenFileDescriptor(project, file)
    descriptor.navigate(true)
  }

  private fun wrapWithTag(tag: String, id: String, content: String): String {
    return "  <$tag id=\"$id\">\n$content\n  </$tag>"
  }

}
